/*************************************************************************
 *                    free                     *
 *************************************************************************
 *************************************************************************
 * Filename	   : section.c
 * Description   : This file contains the functions to process sections
 * Version	   : 1.0
 * History       :
 free 2013-4-16  Create
 *************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include "section.h"
#include "table_pat.h"
#include "table_pmt.h"
#include "package_process.h"

/*************************************************************************
 * Function Name : InsertSection
 * Description   : create a section node behind the section list,
 * 					the section is just filled with byte array
 * Parameters    :
 * Returns
 **************************************************************************/
static void InsertSection(SECTION **ppstSections, unsigned char *pucData)
{
	SECTION *pstTemp = *ppstSections;
	SECTION *pstNew = (SECTION *)malloc(sizeof(SECTION));
	pstNew->pucData = pucData;
	pstNew->pstNext = NULL;
	if (pstTemp == NULL )
	{
		*ppstSections = pstNew;
	}
	else
	{
		while (pstTemp->pstNext)
		{
			pstTemp = pstTemp->pstNext;
		}
		pstTemp->pstNext = pstNew;
	}
}

/*************************************************************************
 * Function Name : ParseSectionHeader
 * Description   : Get the section header from a byte array
 * Parameters    :
 *                pstSectionHeader -- the section header
 *                pucBuffer -- input byte array
 * Returns       :
 **************************************************************************/
static void ParseSectionHeader(SECTION_HEADER *pstSectionHeader, unsigned char *pucBuffer)
{
	pstSectionHeader->table_id = pucBuffer[0];
	pstSectionHeader->section_syntax_indicator = (pucBuffer[1] >> 7) & 0x01;
	pstSectionHeader->reserved_future_use = (pucBuffer[1] >> 6) & 0x1;
	pstSectionHeader->reserved1 = (pucBuffer[1] >> 4) & 0x3;
	pstSectionHeader->section_length = ((pucBuffer[1] & 0x0F) << 8) | pucBuffer[2];
	pstSectionHeader->table_id_extension = (pucBuffer[3] << 8) | pucBuffer[4];
	pstSectionHeader->reserved2 = (pucBuffer[5] >> 6) & 0x03;
	pstSectionHeader->version_number = (pucBuffer[5] >> 1) & 0x1F;
	pstSectionHeader->current_next_indicator = pucBuffer[5] & 0x01;
	pstSectionHeader->section_number = pucBuffer[6];
	pstSectionHeader->last_section_number = pucBuffer[7];
}

/*************************************************************************
 * Function Name : GetFirstDataByteIndex
 * Description   : Get the first data_byte index of a package from a byte array
 * Parameters    :
 *                pstPackageHeader -- the package header
 *                pucBuffer -- input byte array
 * Returns       : -1	failed		>=0	the real index
 **************************************************************************/
static int GetFirstDataByteIndex(PACKAGE_HEADER *pstPackageHeader, unsigned char *pucBuffer)
{
	int iFirstDataByteIndex = -1;
	switch (pstPackageHeader->adaptation_field_control)
	{
		case 0x00:
			return -1;
		case 0x01:
			iFirstDataByteIndex = 4;
			break;
		case 0x02:
			return -1;
		case 0x03:
			iFirstDataByteIndex = 5 + pucBuffer[4];
			break;
		default:
			return -1;
	}
	if (pstPackageHeader->payload_unit_start_indicator == 1)
	{
		iFirstDataByteIndex += (1 + pucBuffer[iFirstDataByteIndex]);
	}
	return iFirstDataByteIndex;
}

/*************************************************************************
 * Function Name : GetSection
 * Description   : Get one section from a file and insert it into the linked list
 * Parameters    :
 *                pSection -- to
 *                pInputFile -- from
 *                iPackageLength -- package length
 *                pliInputOffset -- input file read offset
 * Returns       : 0	failed		1	succeed
 **************************************************************************/
static int GetSection(SECTION **ppstSections, SECTION_HEADER *pstSectionHeader, FILE *pInputFile, int iPackageLength, int iPackagePID,
        int iSectionNumber, int iTableID)
{
	int iFirstDataByteIndex = 0;
	int iCopyDataCount = 0;
	int iRemainingData = 0;
	int iPackageDataCapacity = 0;
	int iContinuityCounter = 0;
	int iSectionDataCount = 0;
	unsigned char aucPackageBuffer[204] = {0};
	unsigned char aucSectionBuffer[4096] = {0};
	unsigned char *pucWritePointer = NULL;
	PACKAGE_HEADER stPackageHeader = {0};

	while (fread(aucPackageBuffer, sizeof(unsigned char), iPackageLength, pInputFile) == iPackageLength)
	{
		GetPackageHeader(&stPackageHeader, aucPackageBuffer);
		if (stPackageHeader.PID != iPackagePID)				//pid not fit
			continue;
		iFirstDataByteIndex = GetFirstDataByteIndex(&stPackageHeader, aucPackageBuffer);
		if (iFirstDataByteIndex == -1)		//no data_byte
			continue;
		if (pucWritePointer == NULL )
		{
			if (stPackageHeader.payload_unit_start_indicator != 1)		//not the section header
				continue;
			ParseSectionHeader(pstSectionHeader, aucPackageBuffer + iFirstDataByteIndex);	//find the section header
			if (pstSectionHeader->section_number != iSectionNumber)			//section number not fit
				continue;
			if (pstSectionHeader->table_id != iTableID)
			{
				continue;
			}
			iSectionDataCount = (pstSectionHeader->section_length) + 3;		//the whole section data sum to read
			iRemainingData = iSectionDataCount;
			pucWritePointer = aucSectionBuffer;
			iContinuityCounter = stPackageHeader.continuity_counter;
		}
		else if (stPackageHeader.continuity_counter != ((iContinuityCounter + 1) & 0x0f)) //we lost packages
		{
			pucWritePointer = NULL;
			continue;
		}
		iPackageDataCapacity = 188 - iFirstDataByteIndex;
		iCopyDataCount = (iRemainingData < iPackageDataCapacity) ? iRemainingData : iPackageDataCapacity; //the data count to copy from current package
		memcpy(pucWritePointer, aucPackageBuffer + iFirstDataByteIndex, iCopyDataCount * sizeof(unsigned char));
		if (iRemainingData <= iPackageDataCapacity)			//get a complete package
		{
			unsigned char *pucSectionData = (unsigned char *)malloc(iSectionDataCount * sizeof(unsigned char));
			memcpy(pucSectionData, aucSectionBuffer, iSectionDataCount * sizeof(unsigned char));
			InsertSection(ppstSections, pucSectionData);
			return 1;
		}
		iRemainingData -= iPackageDataCapacity;
		pucWritePointer += iCopyDataCount;
		iContinuityCounter = stPackageHeader.continuity_counter;
	}
	return 0;
}

/*************************************************************************
 * Function Name : GetSectionList
 * Description   : Get the ordered section list that could be assembled to a completed table
 * Parameters    :
 * Returns       :
 **************************************************************************/
SECTION *GetSectionList(FILE *pInputFile, long int liFirstPackageOffset, int iPackageLength, int iPackagePID, int iTableID)
{
	int iSectionNumber = 0;
	int iVersionNumber = 0;
	int iGetSectionResult = 1;
	SECTION *pstSections = NULL;
	SECTION_HEADER stSectionHeader = {0};

	if (fseek(pInputFile, liFirstPackageOffset, SEEK_SET) != 0)
	{
		printf("File read error !");
		return 0;
	}
	for (;;)
	{
		iGetSectionResult = GetSection(&pstSections, &stSectionHeader, pInputFile, iPackageLength, iPackagePID, iSectionNumber, iTableID);
		if (!iGetSectionResult)
		{
			FreeSectionList(&pstSections);
			break;
		}
		if (iSectionNumber == 0)		//the first section
		{
			iVersionNumber = stSectionHeader.version_number;	//get the version number
		}
		else if (iVersionNumber != stSectionHeader.version_number)	//verify the version number
		{
			iSectionNumber = 0;				//if the version number not fit, reset
			FreeSectionList(&pstSections);
			continue;
		}
		if (stSectionHeader.section_number == stSectionHeader.last_section_number)
		{
			break;
		}
		iSectionNumber++;
	}
	return pstSections;
}

/*************************************************************************
 * Function Name : FreeSectionList
 * Description   : free all the nodes and the data they contained
 * Parameters    :
 * Returns       :
 **************************************************************************/
void FreeSectionList(SECTION **ppstSections)
{
	SECTION *pstTemp = NULL;
	while ((*ppstSections) != NULL )
	{
		pstTemp = *ppstSections;
		*ppstSections = (*ppstSections)->pstNext;
		free(pstTemp->pucData);
		free(pstTemp);
	}
}

